#include "circuitswitching.h"

CircuitSwitching::CircuitSwitching(Network &net,
                                   double   comutionT,
                                   int     &contrFlow,
                                   int     &limM,
                                   QObject *parent) :
  QObject(parent),
  network(net),
  comutionTime(comutionT),
  controllableFlow(contrFlow),
  limMessages(limM),
  curTime(INFINITY),
  sumTime(0.0),
  halfDelay(-1.0),
  totalDelay(-1.0),
  nMessages(0)
{
  core(); // ядро
}

// ядро класса
void CircuitSwitching::core() {
  // найти корень для контролируемого потока, т.е. узел с выходом для контролируемого потока
  Hub *root = findRoot();

  do {
    // поиск лидера
    Signal leader = findLeader(root/*, curTime*/);
//    qDebug() << "Лидер: " << leader.flow;

    curTime = leader.time; // обновить текущее время

//    int i=1;
//    foreach (Flow *flow, network.flows) {
//      qDebug() << "поток: " << i++;
//      qDebug() << "время сообщения:" << flow->messageTime();
//    }

    curTime += comutionTime; // скоммутировать последний канал

    do {
      curTime += network.flows.at(leader.flow-1)->messageSize() / network.capacityChannels; // передать сообщение

      if ( leader.flow == controllableFlow ) {
        sumTime += curTime - network.flows.at(leader.flow-1)->messageTime();
        ++nMessages;
      }
      // определить параметры сообщения следующего за текущим
      network.flows.at(leader.flow-1)->genNewMessageTime(curTime);
      network.flows.at(leader.flow-1)->genNewMessageSize();

      if ( nMessages == limMessages/2 )
        // !!!!!!!!!!!!!! будет заходить сюда кучу раз пока не передастся еще одно сообщение от контролируемого потока
        halfDelay = sumTime / nMessages;  // задержка после передачи половины сообщений контролируемого потока
      else if ( nMessages >= limMessages ) {
        totalDelay = sumTime / nMessages; // задержка после передачи всех сообщений контролируемого потока
        break;
      }

    } while ( network.flows.at(leader.flow-1)->messageTime() <= curTime ); // пока новое сообщение появиляется раньше
                                                                           // времени окончания передачи текущего
    // освободить занятые для передачи каналы
    releasePath(leader.hub, root, curTime);

  } while ( nMessages < limMessages ); // пока не исчерпан лимит передачи

  qDebug() << "\nЗадержка после передачи половины сообщений контролируемого потока: " << halfDelay << endl;
  qDebug() << "Задержка после передачи всех сообщений контролируемого потока: " << totalDelay << endl;
}

// освободить занятые для передачи каналы
// предусловие: узел источника и приемника должны существовать
void CircuitSwitching::releasePath( Hub *s, Hub *d, double time ) {
  if ( s == NULL || d == NULL ) { // проверка предусловия
    error("CircuitSwitching::releasePath(): узел источника или приемника не существует.");
  }

  // пройтись от узла- источника до приемника и освободить выходные каналы
  for ( Hub *hub = s; hub != d; hub = (Hub *)hub->parent() ) {
    hub->outChannel->setReleaseTime(time);
  }
}

// найти корень для контролируемого потока, т.е. узел с выходом для контролируемого потока
// предусловие: контролируемый поток должен быть одним из существующих потоков
// постусловие: корень должен существовать
Hub *CircuitSwitching::findRoot() {
  if ( (controllableFlow <= 0) || (controllableFlow > network.flows.length()) ) // проверка предусловия
    error("CircuitSwitching::findRoot: '" + QString::number(controllableFlow) + "' не существующий номер потока.");

  Hub *root = NULL;
  foreach (root, network.hubs) {
    QVector<int>::const_iterator it = qFind(root->outFlows, controllableFlow);
    if ( it != root->outFlows.constEnd() ) break;
  }

  if (root == NULL) // проверка постусловия
    error("CircuitSwitching::findRoot: поток '" + QString::number(controllableFlow) + "' не найден.");

  return root;
}

// найти лидера
// постусловие: лидер должен существовать
// ????????????????????? неизвестно нужен ли curTime как аргумент
Signal CircuitSwitching::findLeader( Hub *hub/*, double curTime*/) {
//  qDebug() << "корень: " << hub->objectName();
//  qDebug() << "потомки корня: " << hub->children();

  QVector<Signal> lstLeader; // список претендентов на лидерство
  // добавить входящие в данный узел потоки
  foreach (int flow, hub->inFlows) {
    lstLeader.append( Signal(flow, network.flows.at(flow-1)->messageTime(), hub) );
  }

  // найти потомков
  const QObjectList lstChilds = hub->children();

  // определить лидера у потомков
  foreach (QObject *obj, lstChilds) {
    Hub *hub = qobject_cast<Hub *>(obj); // потомок
    Signal sigLeader = findLeader(hub/*, curTime*/);

    // определить текущее время после передачи сигнала-лидера этому узлу
    if ( sigLeader.time <= hub->outChannel->releaseTime() ) { // если выходной канал был занят
      sigLeader.time = hub->outChannel->releaseTime() + comutionTime;
    }
    else {
      sigLeader.time += comutionTime;
    }
    lstLeader.append(sigLeader); // добавить в список претендентов на лидерство
  }

  // найти общего лидера
  Signal leader(-1, -1.0, NULL); // лидер
  switch ( lstLeader.size() ) { // число претендентов на лидерство
  case 0: // проверка постусловия
    error("CircuitSwitching::leader: нету ни одного претендента на лидерство.");
    break;
  case 1:
    leader = lstLeader.first();
    break;
  default: // от двух и более
    QVector<Signal>::iterator it = lstLeader.begin();
    leader = *it;
    // поиск того потока, чье сообщение раньше появилось
    for (++it; it != lstLeader.end(); ++it) {
      if ( (*it).time < leader.time )
        leader = *it;
    }
    break;
  }

  return leader;
}

